1 /* 2 * Collie - An asynchronous event-driven network framework using Dlang development 3 * 4 * Copyright (C) 2015-2017 Shanghai Putao Technology Co., Ltd 5 * 6 * Developer: putao's Dlang team 7 * 8 * Licensed under the Apache-2.0 License. 9 * 10 */ 11 module collie.codec.http.server.responsebuilder; 12 import kiss.logger; 13 import collie.codec.http.server.responsehandler; 14 import collie.codec.http.httpmessage; 15 import collie.codec.http.codec.httpcodec; 16 import collie.codec.http.headers; 17 import kiss.container.ByteBuffer; 18 import std.experimental.allocator.mallocator; 19 20 import std.conv; 21 22 /** 23 */ 24 class ResponseBuilder 25 { 26 this() 27 { 28 _body = new ByteBuffer!Mallocator(); 29 _httpMessage = new HttpMessage(HttpMessageType.response); 30 } 31 32 this(ResponseHandler txn) 33 { 34 _txn = txn; 35 this(); 36 } 37 38 final ResponseBuilder promise(string url, string host) 39 { 40 _httpMessage.url(url); 41 _httpMessage.setHeader(HTTPHeaderCode.HOST, host); 42 return this; 43 } 44 45 final ResponseBuilder status(ushort code, string message) 46 { 47 debug logDebug("status: ", code, " message: ", message); 48 49 _httpMessage.statusCode(code); 50 _httpMessage.statusMessage(message); 51 52 return this; 53 } 54 55 /** 56 * Get the status code for the response. 57 * 58 * @return int 59 */ 60 int status() 61 { 62 return _httpMessage.statusCode(); 63 } 64 65 /** 66 * Set a header on the Response. 67 * 68 * @param string $key 69 * @param array|string $values 70 * @return $this 71 */ 72 final ResponseBuilder header(T = string)(string name, T value) 73 { 74 _httpMessage.setHeader(name, to!string(value)); 75 return this; 76 } 77 78 final ResponseBuilder header(T = string)(HttpHeaderCode code, T value) 79 { 80 _httpMessage.setHeader(code, to!string(value)); 81 return this; 82 } 83 84 /** 85 * Add an array of headers to the response. 86 * 87 * @param array $headers 88 * @return $this 89 */ 90 ResponseBuilder withHeaders(string[string] headers) 91 { 92 validate(); 93 foreach (string key, string value; headers) 94 _httpMessage.addHeader(key, value); 95 return this; 96 } 97 98 final ResponseBuilder setBody(in ubyte[] data) 99 { 100 _body.write(data); 101 return this; 102 } 103 104 final ResponseBuilder connectionClose() 105 { 106 return header(HTTPHeaderCode.CONNECTION, "close"); 107 } 108 109 final void sendWithEOM() 110 { 111 _sendEOM = true; 112 send(); 113 } 114 115 final void send() 116 { 117 validate(); 118 119 bool chunked = true; 120 if (_sendEOM) 121 chunked = false; 122 123 version(CollieDebugMode) 124 { 125 // logDebug("is isResponse : ",_httpMessage.isResponse()); 126 logDebug("resonse status code: ", _httpMessage.statusCode); 127 } 128 129 if (_httpMessage.statusCode >= 200 && _httpMessage.isResponse()) 130 { 131 version (CollieDebugMode) 132 logDebug("Chunked: ", chunked); 133 if (chunked) 134 { 135 _httpMessage.chunked(true); 136 } 137 else 138 { 139 _httpMessage.chunked(false); 140 _httpMessage.setHeader(HTTPHeaderCode.CONTENT_LENGTH, to!string(_body.length)); 141 } 142 } 143 if (_txn) 144 { 145 if ((_body.length == 0) && _sendEOM) 146 { 147 _txn.sendHeadersWithEOM(_httpMessage); 148 return; 149 } 150 else 151 { 152 _txn.sendHeaders(_httpMessage); 153 } 154 } 155 156 if ((_body.length > 0) && _txn) 157 { 158 version (CollieDebugMode) 159 logDebug("body len = ", _body.length); 160 if (chunked) 161 { 162 _txn.sendChunkHeader(_body.length); 163 _txn.sendBody(_body.allData.data()); 164 } 165 else 166 { 167 _txn.sendBody(_body.allData.data(), _sendEOM); 168 return; 169 } 170 _body.clear(); 171 } 172 if (_sendEOM && _txn) 173 { 174 _txn.sendEOM(); 175 } 176 } 177 178 void clear() 179 { 180 _isDisposed = true; 181 _txn = null; 182 } 183 184 private void validate() 185 { 186 assert(!_isDisposed, "The resources have been released!"); 187 } 188 189 @property HttpMessage httpMessage() 190 { 191 return _httpMessage; 192 } 193 194 final @property HttpMessage headers() 195 { 196 return _httpMessage; 197 } 198 199 final @property ByteBuffer!Mallocator* bodys() 200 { 201 return &_body; 202 } 203 204 @property ResponseHandler dataHandler() 205 { 206 return _txn; 207 } 208 209 @property void dataHandler(ResponseHandler txn) 210 { 211 _txn = txn; 212 } 213 214 protected: 215 pragma(inline) final void setResponseHandler(ResponseHandler txn){_txn = txn;} 216 217 protected: 218 HttpMessage _httpMessage; 219 ByteBuffer!Mallocator _body; 220 ResponseHandler _txn; 221 222 private: 223 bool _sendEOM = false; 224 bool _isDisposed = false; 225 }